前面花了這麼多天,我已經看過 MainActivity、ViewModel、Repository、DAO、AppDatabase等檔案,也追過使用者查詢和管理者修改資料的流程。
但如果現在把程式碼全部關掉,只問我一個問題:**「RoomRush 到底是怎麼運作的?」**我能不能不用重新翻檔案,就把它講清楚?
這也是我在第 28 天想做的事情:把前面拆開看的東西重新組起來,畫出 RoomRush 的專案總地圖。
如果要我用一句話介紹 RoomRush,我會說:
RoomRush 是一個以
ClassroomSchedule課表資料為核心,讓一般使用者查詢空教室,也讓管理者維護課表與教室資訊的 Android App。
這句話看起來很簡單,但其實是我重新讀完專案之後才比較能說出口的。
因為一開始我看到的是一個個 Kotlin 檔案,像是 MainActivity.kt、QueryResultViewModel.kt、AppDatabase.kt、ClassroomScheduleDao.kt……
當時比較像是知道這個檔案在做什麼,卻不一定知道它和其他檔案為什麼會連在一起。
重新追過資料流之後,我開始發現,這些檔案其實都圍繞著同一件事情:教室課表資料怎麼進來、怎麼被查詢、怎麼被修改,以及最後怎麼顯示給使用者。
RoomRush
│
┌─────────────┴─────────────┐
│ │
資料來源 App 功能
│ │
SF.csv / ES.csv ┌─────┴─────┐
│ │ │
▼ ▼ ▼
AppDatabase.parseCsv() 使用者功能 管理者功能
│ │ │
▼ │ ▼
ClassroomSchedule │ LoginActivity
│ │ │
▼ │ ▼
Room Database │ ManagerActivity
│ │ │
┌─────┴─────┐ │ ┌─────┼─────┐
▼ ▼ │ ▼ ▼ ▼
DAO Repository │ 課表 教室 教室
│ │ │ 管理 設定 狀態
└─────┬─────┘ │
│ │
▼ │
ViewModel │
│ │
▼ ▼
UI 空教室 / 詳細資訊
把整個專案串起來之後,我發現最值得注意的其實不是哪一個 Activity,而是 ClassroomSchedule。
它代表的是一間教室的一週課表與教室資訊:
ClassroomSchedule
├── classroom:教室名稱,例如 SF304
├── mon1 ~ fri8:一週課表時段
└── classroomType:教室類型,例如 Normal / Computer / Lab
不同功能線其實都在使用同一份 ClassroomSchedule,只是使用角度不同:
| 功能 | 怎麼使用 ClassroomSchedule |
|---|---|
| 查詢空教室 | 判斷指定時段是否 != "X" |
| 課表管理 | 將時段修改成 X 或 null |
| 教室詳細設定 | 修改 classroomType |
| 完整課表 | 找出 == "X" 的時段 |
這也讓我重新理解之前在 Day 9 | 我終於搞懂:在這個專案裡,X 不是空教室
花了一篇文章討論的 X,
以及在 Day 25 | 完整課表頁為什麼顯示的是有課時段,而不是空教室?
有補充到使用者和管理者會看到的差別。
X 的意思其實沒有因為不同頁面而改變,它一直代表「這個時段有課/被佔用」。
差別只是不同功能想回答的問題不同:
X」的教室。X 或取消 X。X」的時段。同一份資料,因為使用情境不同,而產生不同的查詢方式。
MainActivity
↓
QueryResultActivity
↓
QueryResultViewModel
↓
篩選 ClassroomSchedule
↓
EmptyRoomAdapter
↓
空教室列表
↓
RoomDetailActivity
LoginActivity
↓
ManagerActivity
│
├── 課表管理
│
├── 教室設定
│
└── 教室狀態
↓
Repository → DAO
↓
Room Database
SF.csv / ES.csv
↓
AppDatabase.parseCsv()
↓
ClassroomSchedule
↓
Room Database
↓
DAO
↓
Repository
↓
各功能的 ViewModel
看起來這是三條不同的功能,但它們最後都圍繞著同一份 ClassroomSchedule 資料。這也是我重新畫完總圖之後,才比較清楚看到的關係。
當我把這些流程放在同一張圖上時,前面 Day 17 | 我發現一個危險問題:管理者修改可能被 CSV 蓋掉
發現的 CSV 覆蓋問題,也變得更容易理解。
原本我只看到:
CSV → Room Database
但現在把管理者功能也放進來後,就會變成:
SF.csv / ES.csv
↓
Room Database
↑
│
管理者修改
這兩條路徑其實都在操作同一份資料;所以問題就不只是「onOpen() 為什麼會重新匯入 CSV」,而是:當 CSV 和管理者修改都可以成為資料來源時,到底誰才是這份資料的真正來源?
這也是我到後面才發現,原本以為只是初始化資料的 CSV,其實牽涉到整個 App 的資料生命週期。
前幾天使用 Hermes Agent 時,我比較常問的是「這個檔案在做什麼?」或「這個方法的資料從哪裡來?」
到了第 28 天,我問的問題開始不太一樣。我不再只想知道單一檔案,而是要求它把不同功能的流程放在一起比較:
ClassroomSchedule 在不同功能中扮演什麼角色?Hermes 可以很快幫我整理出這些關係,但我沒有直接把它產生的架構圖當成答案。
我還是需要回到實際程式碼或是前面所整理的筆記,確認每一條連線的連結。例如 Activity 傳了什麼 Intent、ViewModel 從哪裡取得 Repository、Repository 實際呼叫哪個 DAO 方法。
整理完整個 RoomRush 專案後,我發現它的核心其實不是某一個畫面,而是一份 ClassroomSchedule 資料。這份資料從 SF.csv 和 ES.csv 匯入 Room Database,經過 DAO 和 Repository,再被不同 ViewModel 提供給畫面使用。
一般使用者看到的是查詢空教室流程:從首頁選大樓,到查詢頁選星期、節次與樓層,最後由 QueryResultViewModel 篩出不是 X 的教室。
管理者看到的是另一面:登入後台後,可以修改某間教室某時段是否有課、修改教室類型、新增教室,或查看每間教室完整的有課時段。
讀完這個專案後,我對 X 的理解也變得更清楚。X 固定代表有課或被佔用,但不同頁面會用不同角度看它。查詢空教室時,程式找的是不是 X;管理者修改課表時,選「是」會寫入 X;完整課表頁則只列出等於 X 的有課時段。
這個專案也暴露出一些工程上的取捨。像是 CSV 每次開啟資料庫時重新匯入,可能覆蓋管理者修改;管理者帳密硬編碼不安全;是否可飲食不是獨立欄位;大樓代碼與時間表硬編碼;以及部分檔案命名不一致。這些不是讓 App 不能運作的錯誤,但很適合作為後續改善清單。
整個空教室查詢系統的操作大致上了解之後,我們在這過程中也提到很多這 App 的缺點。
下一篇我們會一一攤開這些技術債,了解哪些是在真正開發 App 時最需要改良的地方,哪些部分是可以未來可再擴充的功能,並衡量這些改善是否會增加風險?